
/**
\file
Definition of the sun::lua::nmn_lua_interface function.
\author Sean Tromans
*/

#include <sun_pre_compiled.hpp>
#include <sun/lua/needs_more_ninja/needs_more_ninja.hpp>
#include <needs_more_ninja/template/hashed_string.hpp>
#include <boost/preprocessor.hpp>
#include <boost/function.hpp>

#if !defined(SUN_DISABLE_LUA)
#include <sun/lua/lua.hpp>
#endif

#define	NMN_EXPOSE_VECTOR( type )	\
	luabind::class_< type >( BOOST_PP_STRINGIZE( type ) )	\
		.def( luabind::constructor<>( ) )	\
		.def( luabind::constructor<type::value_type>( ) )	\
		.def( luabind::constructor<type const &>( ) )	\
		.property( "size", &type::size )	\
		.def( luabind::const_self + luabind::const_self )	\
		.def( luabind::const_self - luabind::const_self )	\
		.def( luabind::const_self * luabind::const_self )	\
		.def( luabind::const_self * type::value_type() )	\
		/*.def( luabind::const_self / luabind::const_self )*/	\
		.def( luabind::const_self == luabind::const_self )	\
		.def( luabind::tostring(luabind::const_self) )	\
		.def( "strict_equals", &type::strict_equals )	\
		.def( "at", (type::const_reference_type (type::*)(type::size_type) const)&type::at )	\
		.def( "at", (type::reference_type (type::*)(type::size_type))&type::at )	\
		.def( "swap", &type::swap )

#define	NMN_EXPOSE_BOOL_VECTOR( type )	\
	luabind::class_< type >( BOOST_PP_STRINGIZE( type ) )	\
		.def( luabind::constructor<>( ) )	\
		.def( luabind::constructor<type::value_type>( ) )	\
		.def( luabind::constructor<type const &>( ) )	\
		.property( "size", &type::size )	\
		.def( luabind::const_self == luabind::const_self )	\
		.def( luabind::tostring(luabind::const_self) )	\
		.def( "any", (bool (type::*)( ) const)&type::any )	\
		.def( "any", (bool (type::*)( type const & ) const)&type::any )	\
		.def( "all", (bool (type::*)( ) const)&type::all )	\
		.def( "all", (bool (type::*)( type const & ) const)&type::all )	\
		.def( "at", (type::const_reference_type (type::*)(type::size_type) const)&type::at )	\
		.def( "at", (type::reference_type (type::*)(type::size_type))&type::at )	\
		.def( "swap", &type::swap )

#define NMN_DEFINE_VALUE_TYPE_PARAM( z, n, type ) BOOST_PP_COMMA_IF( n ) BOOST_PP_CAT( type, ::value_type )

#define NMN_PARAM_NAME_0 "x"
#define NMN_PARAM_NAME_1 "y"
#define NMN_PARAM_NAME_2 "z"
#define NMN_PARAM_NAME_3 "w"

#define NMN_DEFINE_GET_PARAM( z, n, type ) .property( BOOST_PP_CAT( NMN_PARAM_NAME_, n ), &detail::get_value<type, n>, &detail::set_value<type, n> )

#define	NMN_DEFINE_CONSTRUCTOR_AND_ACCESSORS( name, size ) \
		.def( luabind::constructor< BOOST_PP_REPEAT( size, NMN_DEFINE_VALUE_TYPE_PARAM, BOOST_PP_CAT( name, size ) ) >( ) )	\
		BOOST_PP_REPEAT( size, NMN_DEFINE_GET_PARAM, BOOST_PP_CAT( name, size ) )

#define NMN_EXPOSE_VECTOR_TYPE_SIZE( name, size )	\
		NMN_EXPOSE_VECTOR( BOOST_PP_CAT( name, size ) )	\
		NMN_DEFINE_CONSTRUCTOR_AND_ACCESSORS( name, size )

#define NMN_EXPOSE_BOOL_VECTOR_TYPE_SIZE( name, size )	\
		NMN_EXPOSE_BOOL_VECTOR( BOOST_PP_CAT( name, size ) )	\
		NMN_DEFINE_CONSTRUCTOR_AND_ACCESSORS( name, size )

namespace sun
{

	namespace lua
	{

		namespace detail
		{
			/**
			Helper wrapper function to get a value from a vector at a static index.
			@param v The vector to retreive the value from.
			@return The value at the specified index.
			\author Sean Tromans
			*/
			template < typename type, typename type::size_type index >
			typename type::value_type get_value( type const * v )
			{
				return v->at( index );
			}

			/**
			Helper wrapper function to set a value to a vector at a static index.
			@param v The vector to set the value to.
			@param value The new value to assign.
			\author Sean Tromans
			*/
			template < typename type, typename type::size_type index >
			typename void set_value( type * v, typename type::value_type value )
			{
				v->at( index ) = value;
			}

			/**
			Dummy class to give to lua when exposing the math::constants class.
			\author Sean Tromans
			*/
			struct constants_dummy
			{
				typedef float (*function_type)( );

				/**
				Compile time wrapper of a math::constants static function. Used to expose the value as a property.
				\author Sean Tromans
				*/
				template < function_type f >
				float wrap_function( ) const
				{
					return f( );
				}
			};
		}

		/**
		Exposes the required sections of the needs_more_ninja library to lua.
		@param L The lua state being initialsed.
		\author Sean Tromans
		*/
		void nmn_lua_interface( lua_State * L )
		{
#if !defined(SUN_DISABLE_LUA)
			luabind::module( L, "nmn" )
			[
				luabind::class_< nmn::hashed_string >( "hashed_string" )
					.def( luabind::constructor< nmn::hashed_string::string_const_reference >( ) )
					.def( luabind::constructor< nmn::hashed_string::string_type::value_type const * const >( ) )
					.property( "str", &nmn::hashed_string::str )
					.property( "hash", &nmn::hashed_string::hash )
					.def( luabind::const_self == luabind::const_self )
					.def( luabind::tostring(luabind::const_self) )
			];

			luabind::module( L )
			[
				NMN_EXPOSE_VECTOR_TYPE_SIZE( vec, 2 ),
				NMN_EXPOSE_VECTOR_TYPE_SIZE( vec, 3 ),
				NMN_EXPOSE_VECTOR_TYPE_SIZE( vec, 4 ),

				NMN_EXPOSE_BOOL_VECTOR_TYPE_SIZE( bvec, 2 ),
				NMN_EXPOSE_BOOL_VECTOR_TYPE_SIZE( bvec, 3 ),
				NMN_EXPOSE_BOOL_VECTOR_TYPE_SIZE( bvec, 4 ),

				luabind::class_< quat >( "quat" )
					.def( luabind::constructor<>( ) )
					.def( luabind::constructor<quat::value_type, quat::value_type, quat::value_type, quat::value_type>( ) )
					.def( luabind::constructor<quat const &>( ) )
					.property( "size", &quat::size )
					.def( luabind::const_self + luabind::const_self )
					.def( luabind::const_self - luabind::const_self )
					.def( luabind::const_self * luabind::const_self )
					.def( luabind::const_self * quat::value_type() )
					.def( luabind::const_self == luabind::const_self )
					.def( luabind::tostring(luabind::const_self) )
					.def( "strict_equals", &quat::strict_equals )
					.def( "swap", &quat::swap )
					BOOST_PP_REPEAT( 4, NMN_DEFINE_GET_PARAM, quat )
			];

			luabind::module( L, "sun" )
			[
				luabind::class_< detail::constants_dummy >( "constants_dummy" )
					.property( "epsilon",			&detail::constants_dummy::wrap_function< &sun::math::constantsf::epsilon > )
					.property( "pi",				&detail::constants_dummy::wrap_function< &sun::math::constantsf::pi > )
					.property( "pi_2",				&detail::constants_dummy::wrap_function< &sun::math::constantsf::pi_2 > )
					.property( "pi_4",				&detail::constants_dummy::wrap_function< &sun::math::constantsf::pi_4 > )
					.property( "two_pi",			&detail::constants_dummy::wrap_function< &sun::math::constantsf::two_pi > )
					.property( "e",					&detail::constants_dummy::wrap_function< &sun::math::constantsf::e > )
					.property( "cos_45",			&detail::constants_dummy::wrap_function< &sun::math::constantsf::cos_45 > )
					.property( "cos_pi_4",			&detail::constants_dummy::wrap_function< &sun::math::constantsf::cos_pi_4 > )
					.property( "deg_to_rad_ratio",	&detail::constants_dummy::wrap_function< &sun::math::constantsf::deg_to_rad_ratio > )
					.property( "rad_to_deg_ratio",	&detail::constants_dummy::wrap_function< &sun::math::constantsf::rad_to_deg_ratio > )
			];

			luabind::globals( L )["constants"] = detail::constants_dummy( );
#else
			NMN_UNUSED( L );
#endif
		}
	};
};

#undef NMN_DEFINE_VALUE_TYPE_PARAM
#undef NMN_PARAM_NAME_0
#undef NMN_PARAM_NAME_1
#undef NMN_PARAM_NAME_2
#undef NMN_PARAM_NAME_3
#undef NMN_DEFINE_GET_PARAM
#undef	NMN_DEFINE_CONSTRUCTOR_AND_ACCESSORS
#undef NMN_EXPOSE_VECTOR_TYPE_SIZE
#undef NMN_EXPOSE_BOOL_VECTOR_TYPE_SIZE
#undef	NMN_EXPOSE_BOOL_VECTOR
#undef	NMN_EXPOSE_VECTOR
